package com.upittc.flopol.core;

import java.util.Stack;

import com.upittc.flopol.constant.ErrorConstant;
import com.upittc.flopol.constant.FlopolConstant;
import com.upittc.flopol.core.helper.CoreHelper;

public class Executor {
	
	// PREFIX EXPRESSION
	private Stack <String> stack;
	
	private Stack <String> operand;
	
	private String value;
	
	public String getValue(Stack <String> stack) throws Exception{
		this.stack = stack;
		operand = new Stack <String>();
		if(stack.size() != 0){
			processStack();
		}
		return value;
	}

	private void processStack()throws Exception{
		if(stack.size()== 1){
			value = stack.pop();
		} else {
			while(stack.size() != 0){
				if(stack.peek().equals(FlopolConstant.ADD) || stack.peek().equals(FlopolConstant.SUB) || stack.peek().equals(FlopolConstant.MULT) ||
				   stack.peek().equals(FlopolConstant.DIVI) || stack.peek().equals(FlopolConstant.REMI) || stack.peek().equals(FlopolConstant.EXP) ||
				   stack.peek().equals(FlopolConstant.PING)){
					String token = stack.pop();
					doOperator(token);
				} else if(stack.peek().equals(FlopolConstant.GT) || stack.peek().equals(FlopolConstant.GTE) || stack.peek().equals(FlopolConstant.LT) ||
						   stack.peek().equals(FlopolConstant.LTE) || stack.peek().equals(FlopolConstant.EQ) || stack.peek().equals(FlopolConstant.NEQ)){
					String token = stack.pop();
					doRelational(token);
				} else if (stack.peek().equals(FlopolConstant.INTER) ||stack.peek().equals(FlopolConstant.UNION)
						   || stack.peek().equals(FlopolConstant.NOT)) {
					String token = stack.pop();
					doLogical(token);
				}else {
					String token = stack.peek();
					operand.push(token);
					stack.pop();
				}
			}
			value = operand.pop();
		}
	}
	

	private void doOperator(String token) throws Exception{
		try{
			float eval = 0;
			if(token.equals(FlopolConstant.ADD)){
				eval = Float.parseFloat(operand.pop()) + Float.parseFloat(operand.pop());
			} else if (token.equals(FlopolConstant.SUB)){
				eval = Float.parseFloat(operand.pop()) - Float.parseFloat(operand.pop());
			} else if (token.equals(FlopolConstant.MULT)){
				eval = Float.parseFloat(operand.pop()) * Float.parseFloat(operand.pop());
			} else if (token.equals(FlopolConstant.DIVI)){
				eval = Float.parseFloat(operand.pop()) / Float.parseFloat(operand.pop());
			} else if (token.equals(FlopolConstant.REMI)){
				eval = Float.parseFloat(operand.pop()) % Float.parseFloat(operand.pop());
			} else if (token.equals(FlopolConstant.EXP)){
				eval =  (float) Math.pow(Float.parseFloat(operand.pop()),Float.parseFloat(operand.pop()));
			} else if (token.equals(FlopolConstant.PING)){
				eval = -Float.parseFloat(operand.pop());
			}
			operand.push(Float.toString(eval));
		} catch (Exception e){
			throw new Exception (CoreHelper.errorMessage(ErrorConstant.INVALID_ASSIGN));
		}
	}

	private void doRelational(String token) throws Exception{
		try{
			boolean eval = false;
			if(token.equals(FlopolConstant.GT)){
				eval = Float.parseFloat(operand.pop()) > Float.parseFloat(operand.pop());
			} else if (token.equals(FlopolConstant.GTE)){
				eval = Float.parseFloat(operand.pop()) >= Float.parseFloat(operand.pop());
			} else if (token.equals(FlopolConstant.LT)){
				eval = Float.parseFloat(operand.pop()) < Float.parseFloat(operand.pop());
			} else if (token.equals(FlopolConstant.LTE)){
				eval = Float.parseFloat(operand.pop()) <= Float.parseFloat(operand.pop());
			} else if (token.equals(FlopolConstant.EQ)){
				int val = SemanticAnalyzer.checkValueInput(operand.peek());
				if(val == FlopolConstant.FLT_LIT){
					eval = Float.parseFloat(operand.pop()) == Float.parseFloat(operand.pop());
				} else if (val == FlopolConstant.STR_LIT){
					String holder1 = operand.pop();
					val = SemanticAnalyzer.checkValueInput(operand.peek());
					if(val == FlopolConstant.STR_LIT){
						eval = holder1.equals(operand.peek());
					} else {
						throw new Exception (CoreHelper.errorMessage(ErrorConstant.INVALID_ASSIGN));
					} 
				} else if (val == FlopolConstant.BOL_LIT){
					String holder1 = operand.pop();
					val = SemanticAnalyzer.checkValueInput(operand.peek());
					if(val == FlopolConstant.BOL_LIT){
						eval = holder1.equals(operand.peek());
					} else {
						throw new Exception (CoreHelper.errorMessage(ErrorConstant.INVALID_ASSIGN));
					} 
				} 
			} else if (token.equals(FlopolConstant.NEQ)){
				int val = SemanticAnalyzer.checkValueInput(operand.peek());
				if(val == FlopolConstant.FLT_LIT){
					eval = Float.parseFloat(operand.pop()) != Float.parseFloat(operand.pop());
				} else if (val == FlopolConstant.STR_LIT){
					String holder1 = operand.pop();
					val = SemanticAnalyzer.checkValueInput(operand.peek());
					if(val == FlopolConstant.STR_LIT){
						eval = !holder1.equals(operand.peek());
					} else {
						throw new Exception (CoreHelper.errorMessage(ErrorConstant.INVALID_ASSIGN));
					} 
				} else if (val == FlopolConstant.BOL_LIT){
					String holder1 = operand.pop();
					val = SemanticAnalyzer.checkValueInput(operand.peek());
					if(val == FlopolConstant.BOL_LIT){
						eval = !holder1.equals(operand.peek());
					} else {
						throw new Exception (CoreHelper.errorMessage(ErrorConstant.INVALID_ASSIGN));
					} 
				} 
			}
			operand.push(convertBoolJavaToFlop(eval));
		} catch (Exception e){
			throw new Exception (CoreHelper.errorMessage(ErrorConstant.INVALID_ASSIGN));
		}
	}
	
	private void doLogical(String token) throws Exception {
		try{
			boolean eval = false;
			if(token.equals(FlopolConstant.INTER)){
				eval = convertBoolFlopToJava(operand.pop()) && convertBoolFlopToJava(operand.pop());
			} else if (token.equals(FlopolConstant.UNION)){
				eval = convertBoolFlopToJava(operand.pop()) || convertBoolFlopToJava(operand.pop());
			} else if (token.equals(FlopolConstant.NOT)){
				eval = !convertBoolFlopToJava(operand.pop());
			}
			operand.push(convertBoolJavaToFlop(eval));
		} catch (Exception e){
			throw new Exception (CoreHelper.errorMessage(ErrorConstant.INVALID_ASSIGN));
		}
	}
	
	private String convertBoolJavaToFlop(Boolean val){
		String result = null;
		if(val == true){
			result = FlopolConstant.YEAH;
		} else {
			result = FlopolConstant.NOPE;
		}
		return result;
	}
	
	private Boolean convertBoolFlopToJava(String val){
		Boolean result = false;
		if(val.equals(FlopolConstant.YEAH)){
			result = true;
		} else if (val.equals(FlopolConstant.NOPE)){
			result = false;
		}
		return result;
	}
	
	public void executePrint(int type, Stack <String> stackVal) throws Exception{
		String value = getValue(stackVal);
		value = value.replace(Character.toString(FlopolConstant.DOLLAR),"");
		if(type == FlopolConstant.GIVE_KEY){
			CoreHelper.give(value);
		} else if (type == FlopolConstant.GVMR_KEY){
			CoreHelper.givemore(value);
		}
	}

}
